package com.duanjc.mergemd.service;


import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import com.duanjc.mergemd.bean.DocumentContent;
import com.duanjc.mergemd.task.MergeTask;
import com.duanjc.mergemd.utils.FileCheckUtils;
import com.duanjc.mergemd.utils.FileIOUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.function.BiConsumer;

/**
 * 将多个文件合并成一个文件
 *
 * @author cqh
 * @date 2022/03/13 18:33
 */
@Slf4j
@Service
public class MergeToOneFile extends AbstractMergeFile {
    /**
     * 记录追加的文件个数
     */
    private int fileCount;

    /**
     * 单个合并文件最大文件数
     */
    public static final int FILE_COUNT = 100;


    @Autowired
    private MergeTask mergeTask;

    /*public MergeToOneFile(DocumentContent fileContent) {
        this.suffix = fileContent.getSuffix();
        this.fileCount = fileContent.getInsert();
        this.destPath = fileContent.getDestPath();
        this.srcPath = fileContent.getSrcPath();
    }*/

    /**
     * 在插入第 fileCount 个的文件时前加入 [TOC]
     *
     * @param bufferedWriter 使用此实例在 dest 中写入目录
     * @param dest           要操作的文件
     * @throws IOException 读取文件或写入失败抛出此异常
     */
    @Override
    public void insertToDirectory(BufferedWriter bufferedWriter, File dest) throws IOException {
        fileCount--;
        if (0 == fileCount) {
            bufferedWriter.write("[TOC]\n");
        }
    }

    /**
     * 得到 srcPath 下所有指定后缀的文件，将其合并成一个文件，放在 destPath 下
     *
     * @throws IOException 详看{@link AbstractMergeFile#mergeFile(File, File)}方法抛出的 {@code IOException} 说明
     */
    @Override
    public void operationMerge() throws IOException {
        this.fileCount = 1;
        if (!FileCheckUtils.matches(destPath, suffix)) {
            log.error("后缀格式错误或者指定后缀与目标后缀不匹配");
            return;
        }
        File src = new File(srcPath);
        File dest = new File(destPath);
        File parentFile = dest.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        FileIOUtils.clear(dest);
        List<File> fileList = FileUtil.loopFiles(src);
        if (fileList.size() > FILE_COUNT) {
            log.info("文件数大于100，进行分片合并！");
            List<List<File>> list = ListUtil.partition(fileList, FILE_COUNT);
            this.fileCount = list.size();
            CountDownLatch latch = new CountDownLatch(list.size());
            forEach(list, (i, item) -> {
                String path = dest.getParent();
                String fileName = dest.getName().replace(this.suffix, "");
                String newFileName = fileName + i + this.suffix;
                File newFile = new File(path + FileUtil.FILE_SEPARATOR + newFileName);
                mergeTask.partitionMerge(item, newFile, this.suffix, latch);
            });
            try {
                log.info("等待合并完成！");
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        } else {
            mergeFile(src, dest);
        }

        log.info("文件合并成完成！共计[{}]个文件！", fileCount);
    }

    /**
     * description: setDocumentContent <br>
     * version: 1.0 <br>
     * date: 2022/11/24 17:59 <br>
     * author: duanjiangcheng <br>
     *
     * @param documentContent documentContent
     * @return void
     */
    @Override
    public void setDocumentContent(DocumentContent documentContent) {
        this.suffix = documentContent.getSuffix();
        this.fileCount = documentContent.getInsert();
        this.destPath = documentContent.getDestPath();
        this.srcPath = documentContent.getSrcPath();
    }

    @Async("threadPoolTaskExecutor")
    public void partitionMerge(List<File> list, File out, CountDownLatch downLatch) {
        log.info("线程-" + Thread.currentThread().getName() + ":正在进行分片导出！");
        BufferedWriter bufferedWriter = null;
        try {
            bufferedWriter = FileUtil.getWriter(out, StandardCharsets.UTF_8, true);

            for (File file : list) {
                String fileName = file.getName();
                if (FileCheckUtils.matches(fileName, suffix)) {
                    BufferedReader bufferedReader = FileUtil.getUtf8Reader(file);
                    String line = null;
                    try {
                        insertToDirectory(bufferedWriter, out);
                        while ((line = bufferedReader.readLine()) != null) {
                            bufferedWriter.write(line);
                            bufferedWriter.newLine();
                        }
                        bufferedWriter.newLine();
                        bufferedWriter.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            bufferedReader.close();
                        } catch (IOException e) {
                            log.error("关闭输入流失败！", e);
                        }
                    }
                }
            }
        } finally {
            try {
                bufferedWriter.close();
            } catch (IOException e) {
                log.error("关闭输出流失败！", e);
            }
            downLatch.countDown();
        }
    }

    /**
     *
     * @param elements 需要遍历集合的数据
     * @param action 执行的方法
     * @param <T>
     */
    public static <T> void forEach(Iterable<? extends T> elements, BiConsumer<Integer, ? super T> action) {
        Objects.requireNonNull(elements);
        Objects.requireNonNull(action);
        Integer index = 0;
        for (T t : elements) {
            action.accept(index, t);
            index++;
        }
    }
}
